package com.qf.commons.redis.aspect;

import com.qf.commons.redis.annotation.CLock;
import com.qf.commons.redis.utils.MyLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 分布式锁的切面类
 * author Ken
 * create_time 2022/11/23
 */
@Aspect
@Component
public class MyLockApsect implements Ordered {

    private ExpressionParser expressionParser = new SpelExpressionParser();

    private LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    //execution(* com.qf.*.*Controller.*(..))
    //@annotation() 被标记了某个注解方法会被增强
    //@within() 被标记了某个注解的类 下的所有方法都会被增强
    @Around("@annotation(com.qf.commons.redis.annotation.CLock)")
    public Object lockHandler(ProceedingJoinPoint joinPoint) throws Throwable {

        //通过反射获得注解 获取注解上的 锁的名称
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        CLock cLock = method.getAnnotation(CLock.class);
        String spelStr = cLock.value(); //获取spel表达式

        //获取整个方法的形参名称
        String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(method);

        //获取方法的形参的值
        Object[] args = joinPoint.getArgs();

        //组装一个容器
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            evaluationContext.setVariable(parameterNames[i], args[i]);
        }
        //spel表达式的解析
        Expression expression = expressionParser.parseExpression(spelStr);
        String lockName = expression.getValue(evaluationContext, String.class);

        try {
            MyLock.lock(lockName);
            //放行 - 核心业务
            Object result = joinPoint.proceed(args);
            return result;
        } finally {
            MyLock.unlock();
        }
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
